using System;
using System.Diagnostics;
using System.IO;
using Microsoft.WowAddonStudio.Lua.RuntimeService.ShellHost;
using Microsoft.WowAddonStudio.Lua.RuntimeService.WowBench;

namespace Microsoft.WowAddonStudio.Lua.RuntimeService.ShellHost
{
	/// <summary>
	/// Implementation of IShellHost for Windows Command Shell (cmd.exe) and Lua.exe.
	/// </summary>
	/// <remarks>
	/// To host cmd.exe, an actual cmd.exe process is started where its output, input and error streams are redirected to stream properties
	/// required by IShellHost.To issue commands to shell, that command is directly written in to input stream. 
	/// 
	/// When we keep a running copy of cmd.exe, we gain the capability to allow environment variables that user set during shell interaction
	/// to be persistent. But we are not allowed to access those environment variables using Process object, thus ExpandEnvironmentVariables
	/// method is not implemented for cmd.exe host.
	/// </remarks>
	[Serializable]
	public sealed class AsyncCommandShellHost : BaseShellHost
	{

		#region IShellHost Members


		/// <summary>
		/// Executes command in shell.
		/// </summary>
		/// <param name="command">The command string.</param>
		public override void ExecuteCommand(string command)
		{
			//We need to convert the input string into the proper encoding for cmd.exe (ibm850)
			var streamWriter = new StreamWriter(InputStream.BaseStream, consoleEncoding);
			streamWriter.WriteLine(command);
			streamWriter.Flush();
		}


		/// <summary>
		/// This method is called when ShellTextBox creates the host instance for the first time. 
		/// </summary>
		/// <param name="startingDirectory">Preferred directory to start in.</param>
		/// <param name="outputWriter">The <see cref="IOutputWriter"/> parameter.</param>
		/// <param name="executableName">The name of executable to load.</param>
		/// <remarks>This method will only be called once at the beginning.</remarks>
		public override bool LaunchShellProcess(IOutputWriter outputWriter, string startingDirectory, string executableName)
		{
			return LaunchShellProcess(outputWriter, startingDirectory, executableName, string.Empty);
		}

		/// <summary>
		/// This method is called when ShellTextBox creates the host instance for the first time. 
		/// </summary>
		/// <param name="startingDirectory">Preferred directory to start in.</param>
		/// <param name="outputWriter">The <see cref="IOutputWriter"/> parameter.</param>
		/// <param name="executableName">The name of executable to load.</param>
		/// <param name="argument">The argument for executable.</param>
		/// <remarks>This method will only be called once at the beginning.</remarks>
		public override bool LaunchShellProcess(IOutputWriter outputWriter, string startingDirectory, string executableName, string argument)
		{
			lock (syncObject)
			{
				shellName = executableName;
				arguments = argument;
				writer = outputWriter;

				// Create cmd.exe, turning off the window and redirecting I/O to us
				var processStartInfo = new ProcessStartInfo(ShellName)
				                       	{
				                       		Arguments = arguments,
				                       		CreateNoWindow = true,
				                       		RedirectStandardError = true,
				                       		RedirectStandardInput = true,
				                       		RedirectStandardOutput = true,
				                       		UseShellExecute = false
				                       	};

				shellProcess = Process.Start(processStartInfo);
				bool result = shellProcess != null;

				if (result)
				{
					SetRedirectedStreamsUnBuffered();
					ShellState = ShellHostState.Running;
					shellProcess.OutputDataReceived += Process_OutputDataReceived;
					shellProcess.ErrorDataReceived += Process_ErrorDataReceived;

					InputStream.AutoFlush = true;
					shellProcess.EnableRaisingEvents = true;
					shellProcess.Exited += ProcessExited;

					shellProcess.BeginErrorReadLine();
					shellProcess.BeginOutputReadLine();
				}
				return result;
			}
		}

		#endregion


		/// <summary>
		/// Handles the ErrorDataReceived event of the Process control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Diagnostics.DataReceivedEventArgs"/> instance containing the event data.</param>
		private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
		{
			Debug.WriteLine("Process_ErrorDataReceived:" + e.Data);

			writer.WriteLineFormat(e.Data);
		}


		/// <summary>
		/// Handles the OutputDataReceived event of the Process control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Diagnostics.DataReceivedEventArgs"/> instance containing the event data.</param>
		private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
		{
			Debug.WriteLine("Process_OutputDataReceived:" + e.Data);

			writer.WriteLineFormat(e.Data);
		}

		/// <summary>
		/// This method is called when shell has exited and it should be restarted
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ProcessExited(object sender, EventArgs e)
		{
			Debug.WriteLine(String.Format("Process Exited...[{0}]", shellProcess.ExitCode));

			ShellState = ShellHostState.Closed;
			RaiseOnProcessExited();

			lock (syncObject)
			{
				shellProcess.Close();
				LaunchShellProcess(writer, null, ShellName);
			}
		}

	}
}